use super::{err, err_value, throw_err, ECode, Value};
use rseip::precludes::{AbEipClient, MessageService, PortSegment};
use s7::{client, tcp, transport::Connection::PG};
use std::net::{IpAddr, Ipv4Addr};
use std::time::Duration;

pub type S7Client = client::Client<tcp::Transport>;

pub struct Plc {
    cip: Option<AbEipClient>,
    s7: Option<S7Client>,
}

impl Plc {
    pub fn new() -> Self {
        Self {
            cip: None,
            s7: None,
        }
    }

    pub fn get_s7(&mut self) -> Option<&mut S7Client> {
        self.s7.as_mut()
    }

    pub fn close_s7(&mut self) {
        let stream = self.s7.take();

        if let Some(mut v) = stream {
            let _ = v.stop();
        }
    }

    pub fn set_s7(&mut self, ip: &str, rank: i64, slot: i64) -> Result<(), Value> {
        if let None = self.s7 {
            let addr: Ipv4Addr = match ip.parse() {
                Ok(v) => v,
                Err(e) => {
                    throw_err!(ECode::Connect, e);
                }
            };

            let mut opts = tcp::Options::new(IpAddr::from(addr), rank as u16, slot as u16, PG);

            opts.read_timeout = Duration::from_secs(2);
            opts.write_timeout = Duration::from_secs(2);
        }

        Ok(())
    }

    pub fn get_cip(&mut self) -> Option<&mut AbEipClient> {
        self.cip.as_mut()
    }

    pub async fn close_cip(&mut self) {
        let stream = self.cip.take();

        if let Some(mut v) = stream {
            let _ = v.close().await;
        }
    }

    pub async fn set_cip(&mut self, ip: &str) -> Result<(), Value> {
        if let None = self.cip {
            let tcp = match AbEipClient::new_host_lookup(ip).await {
                Ok(v) => v.with_connection_path(PortSegment::default()),
                Err(e) => {
                    throw_err!(ECode::Connect, e);
                }
            };

            self.cip = Some(tcp);
        }

        Ok(())
    }
}
